package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestNumericPredicatesComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		// exact? tests
		{"(exact? 1)", "#t"},
		{"(exact? 1/2)", "#t"},
		{"(exact? 1.5)", "#f"},
		
		// inexact? tests
		{"(inexact? 1)", "#f"},
		{"(inexact? 1/2)", "#f"},
		{"(inexact? 1.5)", "#t"},
		
		// odd? tests
		{"(odd? 3)", "#t"},
		{"(odd? 4)", "#f"},
		{"(odd? -3)", "#t"},
		{"(odd? -4)", "#f"},
		
		// even? tests
		{"(even? 4)", "#t"},
		{"(even? 3)", "#f"},
		{"(even? -4)", "#t"},
		{"(even? -3)", "#f"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

func TestNumericComparisonComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		// max tests
		{"(max 1 2 3)", "3"},
		{"(max 1/2 2/3 3/4)", "3/4"},
		{"(max 1.5 2.7 0.3)", "2.7"},
		{"(max -1 -2 -3)", "-1"},
		
		// min tests
		{"(min 1 2 3)", "1"},
		{"(min 1/2 2/3 3/4)", "1/2"},
		{"(min 1.5 2.7 0.3)", "0.3"},
		{"(min -1 -2 -3)", "-3"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

func TestNumericArithmeticComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		// abs tests
		{"(abs -5)", "5"},
		{"(abs -3/4)", "3/4"},
		{"(abs -1.5)", "1.5"},
		{"(abs 5)", "5"},
		{"(abs 3/4)", "3/4"},
		{"(abs 1.5)", "1.5"},
		
		// quotient tests
		{"(quotient 10 3)", "3"},
		{"(quotient -10 3)", "-3"},
		{"(quotient 10 -3)", "-3"},
		{"(quotient -10 -3)", "3"},
		
		// remainder tests
		{"(remainder 10 3)", "1"},
		{"(remainder -10 3)", "-1"},
		{"(remainder 10 -3)", "1"},
		{"(remainder -10 -3)", "-1"},
		
		// modulo tests
		{"(modulo 10 3)", "1"},
		{"(modulo -10 3)", "2"},
		{"(modulo 10 -3)", "-2"},
		{"(modulo -10 -3)", "-1"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

func TestGcdLcmComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		// gcd tests
		{"(gcd)", "0"},
		{"(gcd 12)", "12"},
		{"(gcd -12)", "12"},
		{"(gcd 12 18)", "6"},
		{"(gcd 12 18 24)", "6"},
		{"(gcd 0 5)", "5"},
		{"(gcd 5 0)", "5"},
		{"(gcd 10 15 25)", "5"},
		
		// lcm tests
		{"(lcm)", "1"},
		{"(lcm 12)", "12"},
		{"(lcm -12)", "12"},
		{"(lcm 12 18)", "36"},
		{"(lcm 12 18 24)", "72"},
		{"(lcm 0 5)", "0"},
		{"(lcm 5 0)", "0"},
		{"(lcm 4 6 8)", "24"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

func TestRoundingProceduresComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		// floor tests
		{"(floor 3.7)", "3"},
		{"(floor -3.7)", "-4"},
		{"(floor 3)", "3"},
		{"(floor 3/4)", "0"},
		{"(floor -3/4)", "-1"},
		
		// ceiling tests
		{"(ceiling 3.7)", "4"},
		{"(ceiling -3.7)", "-3"},
		{"(ceiling 3)", "3"},
		{"(ceiling 3/4)", "1"},
		{"(ceiling -3/4)", "0"},
		
		// truncate tests
		{"(truncate 3.7)", "3"},
		{"(truncate -3.7)", "-3"},
		{"(truncate 3)", "3"},
		{"(truncate 3/4)", "0"},
		{"(truncate -3/4)", "0"},
		
		// round tests
		{"(round 3.7)", "4"},
		{"(round 3.2)", "3"},
		{"(round -3.7)", "-4"},
		{"(round -3.2)", "-3"},
		{"(round 3)", "3"},
		{"(round 3/4)", "1"},
		{"(round 1/4)", "0"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

func TestRationalizeComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"(rationalize 0 0)", "0"},
		{"(rationalize 1/3 1/10)", "1/3"},
		{"(rationalize 1 1/10)", "1"},
		{"(rationalize 0.3 0.1)", "1/3"},
		{"(rationalize 0 1)", "0"},
		{"(rationalize 3 1)", "3"},
		{"(rationalize 1.5 0.25)", "3/2"},
		{"(rationalize -0.5 0.1)", "-1/2"},
		{"(rationalize 1/2 1/4)", "1/2"},
		{"(rationalize 7/8 1/8)", "1"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

func TestExpLogComprehensive(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"(exp 0)", "1.0"},
		{"(exp 1)", "2.718281828459045"},
		{"(exp 2)", "7.38905609893065"},
		{"(exp 1/2)", "1.6487212707001282"},
		{"(exp -1)", "0.36787944117144233"},
		{"(log 1)", "0.0"},
		{"(log 2.718281828459045)", "1.0"},
		{"(log 2/3)", "-0.40546510810816444"},
		{"(log 7.38905609893065)", "2.0"},
		{"(log 0)", "-Inf.0"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}
